package com.yidin.util;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.HttpVersion;
import org.apache.http.NameValuePair;
import org.apache.http.StatusLine;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.params.ConnManagerParams;
import org.apache.http.conn.params.ConnPerRouteBean;
import org.apache.http.conn.params.ConnRoutePNames;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.HTTP;

import android.app.Activity;
import android.content.Context;
import android.webkit.WebSettings;
import android.webkit.WebView;

/**
 * 需要权限<uses-permission android:name="android.permission.INTERNET" />
 * 
 * @author cola
 * @since 20120718
 */
public class HttpAdapter {
	private static final String TAG = "HttpAdapter";

	private static final boolean IS_LOG = true;

	private static final String CHARSET = HTTP.UTF_8;

	private static HttpAdapter mSingleInstance = null;
	private int mMyPid;
	private Context ctx;

	private DefaultHttpClient mHttpClient;
	private List<HttpRequestThread> mThreadList = Collections
			.synchronizedList(new ArrayList<HttpRequestThread>());

	private String localLogPath = null;
	
	/**
	 * 生成请求头部
	 * @return List<NameValuePair>
	 */
	static public List<NameValuePair> getHttpEntity(){
		List<NameValuePair> NV =new ArrayList<NameValuePair>();
		return NV;
		
	}

	/**
	 * 获取URL中的文件名
	 * @return 文件名
	 */
	static public String getUrlFileName(String url) {
		AspLog.d(TAG, url);
		if (url.length() > 2)
			url = url.substring(url.lastIndexOf("/") + 1, url.length());
		return url;
	}
	
	static public HttpAdapter getDefault(Context context) {
		int mypid = android.os.Process.myPid();
		if (mSingleInstance == null || mypid != mSingleInstance.mMyPid) {
			mSingleInstance = new HttpAdapter(context);
		}
		return mSingleInstance;
	}

	static public void shutdown() {
		if (mSingleInstance != null) {
			mSingleInstance.mHttpClient.getConnectionManager().shutdown();
			mSingleInstance = null;
		}
	}

	private HttpAdapter(Context context) {
		ctx = context;
		mMyPid = android.os.Process.myPid();

		if (FileAdapter.isSdCardExist()) {
			localLogPath = FileAdapter.getSdCardRootDir();
		} else {
			localLogPath = FileAdapter.getFilesDir(ctx);
		}
		localLogPath += "HttpRespLog.xml";

		HttpParams params = new BasicHttpParams();
		// 设置一些基本参数
		HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
		HttpProtocolParams.setContentCharset(params, CHARSET);
		// HttpProtocolParams.setUseExpectContinue(params, true);
		ConnManagerParams.setMaxTotalConnections(params, 60);
		ConnPerRouteBean connPerRoute = new ConnPerRouteBean(30);
		ConnManagerParams.setMaxConnectionsPerRoute(params, connPerRoute);
		// params.setParameter(ClientPNames.CONNECTION_MANAGER_FACTORY, new
		// MMConnectionManagerFactory());

		// 设置我们的HttpClient支持HTTP和HTTPS两种模式
		SchemeRegistry schReg = new SchemeRegistry();
		schReg.register(new Scheme("http", PlainSocketFactory
				.getSocketFactory(), 80));
		// schReg.register(new Scheme("https",
		// SSLSocketFactory.getSocketFactory(), 443));

		// 使用线程安全的连接管理来创建HttpClient
		ClientConnectionManager conMgr = new ThreadSafeClientConnManager(
				params, schReg);
		mHttpClient = new DefaultHttpClient(conMgr, params);
		// mHttpClient.setReuseStrategy(new NoConnectionReuseStrategy());//
		// lhy:实测中，重用策略会导致一些网络异常错误
	}

	/**
	 * 回调请求结果
	 * @author cola
	 *
	 */
	public interface OnHttpResponse {
		/**
		 * 返回服务器响应结果
		 * @param url 链接
		 * @param isOk
		 * @param err 错误代码
		 * @param size 服务器返回长度
		 * @param inStream 服务器返回数据
		 */
		public void onResponse(String url, boolean isOk, String err, long size,
				InputStream inStream);
	}

	public void requestAsync(String url, HttpHost proxy,
			Map<String, String> headers, boolean post,
			OnHttpResponse response) {

		HttpRequestThread thread = new HttpRequestThread(url, proxy, headers,
				post, response);
		mThreadList.add(thread);
		try {
			thread.start();
		} catch (Exception e) {
			e.printStackTrace();
			AspLog.e(TAG, "requestAsync exception", e);
		}

		resetDefaultTimeout();
	}

	public void cancel(String url) {
		if (url == null || mThreadList.size() == 0)
			return;

		CopyOnWriteArrayList<HttpRequestThread> threads = new CopyOnWriteArrayList<HttpRequestThread>(
				mThreadList);
		for (HttpRequestThread thread : threads) {
			if (thread == null) {
				mThreadList.remove(thread);
				continue;
			}
			if (thread.url != null && thread.url.equals(url)) {
				thread.abort();
				thread.interrupt();
				mThreadList.remove(thread);
				return;
			}
		}
	}

	/**
	 * 停止所有在请示的线程
	 */
	public void cancelAll() {
		CopyOnWriteArrayList<HttpRequestThread> threads = new CopyOnWriteArrayList<HttpRequestThread>(
				mThreadList);
		mThreadList.clear();
		for (HttpRequestThread thread : threads) {
			if (thread == null)
				continue;
			thread.abort();
			thread.interrupt();
		}
	}

	public static int DEF_CONNECTION_TIMEOUT = 15000;
	public static int DEF_RECEIVE_TIMEOUT = -1;

	public void setConnectionTimeout(int timeout) {
		synchronized (HttpAdapter.class) {
			HttpAdapter.DEF_CONNECTION_TIMEOUT = timeout;
		}
	}

	public void setReceiveTimeout(int timeout) {
		synchronized (HttpAdapter.class) {
			HttpAdapter.DEF_RECEIVE_TIMEOUT = timeout;
		}
	}

	private void resetDefaultTimeout() {
		synchronized (HttpAdapter.class) {
			HttpAdapter.DEF_CONNECTION_TIMEOUT = 15000;
			HttpAdapter.DEF_RECEIVE_TIMEOUT = -1;
		}
	}

	private int getDefaultConnectionTimeout() {
		synchronized (HttpAdapter.class) {
			return HttpAdapter.DEF_CONNECTION_TIMEOUT;
		}
	}

	private int getDefaultReceiveTimeout() {
		synchronized (HttpAdapter.class) {
			return HttpAdapter.DEF_RECEIVE_TIMEOUT;
		}
	}

	final private class HttpRequestThread extends Thread {
		private String url;
		private HttpHost proxy;
		private Map<String, String> headers;
		private boolean post;
		private OnHttpResponse response;
		private HttpRequestBase httpRequest;
		private int receiveTimeout;
		private int connectTimeout;

		public HttpRequestThread(String url, HttpHost proxy,
				Map<String, String> headers, boolean post,
				OnHttpResponse response) {
			super();

			this.url = url;
			this.proxy = proxy;
			this.headers = headers;
			this.post = post;
			this.response = response;
			this.receiveTimeout = getDefaultReceiveTimeout();
			this.connectTimeout = getDefaultConnectionTimeout();
		}

		public void abort() {
			synchronized (this) {
				if (httpRequest != null) {
					httpRequest.abort();
					httpRequest = null;
				}
			}
			response.onResponse(url, false, "user cancel", 0, null);
		}

		@Override
		public void run() {
			request();
		}

		private void request() {
			AspLog.d(TAG, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");

			if (response == null) {
				AspLog.e(TAG, "OnHttpResponse is null");
				return;
			}
			List<NameValuePair> nvps = new ArrayList<NameValuePair>();
			try {
				// 处理Get、Post请求
				HttpUriRequest request;
				if (post == false) {
					AspLog.d(TAG, "GET : " + url);
					synchronized (this) {
						httpRequest = new HttpGet(url);
						
						request = httpRequest;
					}
				} else {
					AspLog.d(TAG, "POST : " + url);
					synchronized (this) {
						HttpPost p = new HttpPost(url);
						httpRequest = p;
						request = httpRequest;
					}
				}

				// 设置参数
				HttpParams params = mHttpClient.getParams();
				if (proxy != null) {
					AspLog.d(TAG, "proxy host: " + proxy.getHostName()
							+ ", port: " + proxy.getPort());
					params.setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);
				} else {
//					HttpHost proxyHost = NetAdapter.getProxy(ctx);
//					if (proxyHost == null)
//						params.removeParameter(ConnRoutePNames.DEFAULT_PROXY);
//					else
//						params.setParameter(ConnRoutePNames.DEFAULT_PROXY,
//								proxyHost);
				}
				// 超时设置
				/* 从连接池中取连接的超时时间 */
				if (connectTimeout > 0) {
					HttpConnectionParams.setConnectionTimeout(params,
							connectTimeout);
				} else {
					HttpConnectionParams.setConnectionTimeout(params, 0);
				}
				/* 请求超时 */
				if (receiveTimeout > 0) {
					HttpConnectionParams.setSoTimeout(params, receiveTimeout);
				} else {
					HttpConnectionParams.setSoTimeout(params, 0);
				}

				if (headers != null) {
					for (Map.Entry<String, String> nv : headers.entrySet()) {
						if (nv.getKey() != null && nv.getValue() != null) {
							AspLog.d(TAG, "httpheader name: " + nv.getKey()
									+ ", value: " + nv.getValue());
							request.addHeader(nv.getKey(), nv.getValue());
						}
					}
				}

				// 网络通讯结果处理
				HttpResponse httpResponse = null;
				int maxRetryTime = 3;
				int retryTime = 0;
				while (retryTime < maxRetryTime) {
					retryTime++;
					try {
						
						httpResponse = mHttpClient.execute(request);
						if (httpResponse != null) {
							if (httpResponse.getStatusLine() != null)
								break;
						}
					} catch (Exception e) {
						e.printStackTrace();
					}
					// 出异常时，关闭多余的空闲连接能增加重试成功率
					(mHttpClient.getConnectionManager()).closeIdleConnections(
							1, TimeUnit.SECONDS);
					// 重试越多，等待再试时间越长
					sleep(1000 * retryTime);
				}
				if (retryTime == maxRetryTime) {
					response.onResponse("", false, "user cancel", 0, null);
					throw new SocketTimeoutException("网络或服务器无响应(重试" + retryTime
							+ "次)");
				}

				StatusLine statusLine = httpResponse.getStatusLine();
				if (statusLine == null) {
					throw new Exception("Http StatusLine is null");
				}
				int statusCode = statusLine.getStatusCode();
				if (statusCode != HttpStatus.SC_OK
						&& statusCode != HttpStatus.SC_PARTIAL_CONTENT) {
					throw new Exception("Http Status text: "
							+ statusLine.getStatusCode()
							+ statusLine.getReasonPhrase());
				}

				HttpEntity resEntity = httpResponse.getEntity();
				if (resEntity == null) {
					throw new Exception("Http Entity is null");
				}

				InputStream is = null;
				try {
					is = resEntity.getContent();
					if (is == null)
						responseErr(response, url,
								"HttpEntity getContent is null");
					else {
						long size = resEntity.getContentLength();
						if (IS_LOG && size > 0 && size < 102400) {
							FileAdapter.writeFile(localLogPath, is);
							is.close();
							is = FileAdapter.readFile(localLogPath);
						}
						responseOk(response, url, size, is);
					}
				} catch (IllegalStateException e) {
					responseErr(
							response,
							url,
							"HttpEntity getContent IllegalStateException: "
									+ e.getMessage());
				} catch (IOException e) {
					responseErr(
							response,
							url,
							"HttpEntity getContent IOException: "
									+ e.getMessage());
				}

				// 打印响应包
				if (IS_LOG) {
					try {
						Header[] rspHeaders = httpResponse.getAllHeaders();
						for (Header h : rspHeaders) {
							AspLog.i(TAG, h.toString());
						}
						long size = resEntity.getContentLength();
						if (size > 0 && size < 102400) {
							is.close();
							is = FileAdapter.readFile(localLogPath);
							String str = convertStreamToString(is);
							AspLog.d(TAG, "Entity " + str);
						}
					} catch (Exception e) {
						e.printStackTrace();
					}
				}

				// 资源回收
				try {
					if (is != null) {
						is.close();
					}
				} catch (IOException e) {
				}
				try {
					resEntity.consumeContent();
				} catch (IOException e) {
					synchronized (this) {
						if (httpRequest != null)
							httpRequest.abort();
					}
				}

			} catch (IllegalArgumentException e) {
				synchronized (this) {
					if (httpRequest != null)
						httpRequest.abort();
				}
				AspLog.e(TAG,
						"request IllegalArgumentException: " + e.getMessage());
				responseErr(response, url, "request IllegalArgumentException: "
						+ e.getMessage());
			} catch (ClientProtocolException e) {
				synchronized (this) {
					if (httpRequest != null)
						httpRequest.abort();
				}
				AspLog.e(TAG,
						"request ClientProtocolException: " + e.getMessage());
				responseErr(response, url, "request ClientProtocolException: "
						+ e.getMessage());
				// e.toString();
				// e.printStackTrace();
			} catch (IOException e) {
				synchronized (this) {
					if (httpRequest != null)
						httpRequest.abort();
				}
				AspLog.e(TAG, "request IOException: " + e.getMessage());
				responseErr(response, url,
						"request IOException: " + e.getMessage());
				// 出异常时，关闭多余的空闲连接能增加重试成功率
				// (mHttpClient.getConnectionManager()).closeIdleConnections(1,
				// TimeUnit.SECONDS);
			} catch (Exception e) {
				synchronized (this) {
					if (httpRequest != null)
						httpRequest.abort();
				}
				AspLog.e(TAG, "request Exception: " + e.getMessage());
				responseErr(response, url,
						"request Exception: " + e.getMessage());
			}

			AspLog.d(TAG, "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<");
		}

	}

	private void responseErr(OnHttpResponse response, String url, String err) {
		try {
			response.onResponse(url, false, err, 0, null);
		} catch (Exception e) {
			e.printStackTrace();
			AspLog.e(TAG, "onResponse exception", e);
		}
	}

	private void responseOk(OnHttpResponse response, String url, long size,
			InputStream inStream) {
		try {
			response.onResponse(url, true, null, size, inStream);
		} catch (Exception e) {
			e.printStackTrace();
			AspLog.e(TAG, "onResponse exception", e);
		}
	}

	private String convertStreamToString(InputStream is) {
		/*
		 * To convert the InputStream to String we use the
		 * BufferedReader.readLine() method. We iterate until the BufferedReader
		 * return null which means there's no more data to read. Each line will
		 * appended to a StringBuilder and returned as String.
		 */
		BufferedReader reader = new BufferedReader(new InputStreamReader(is));
		StringBuilder sb = new StringBuilder();

		String line = null;
		try {
			while ((line = reader.readLine()) != null) {
				sb.append(line + "\n");
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				is.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

		return sb.toString();
	}
	
	/**
	 * 返回url中的文件名
	 * @param url
	 * @return
	 */
	public static String getNameFromUrl(String url){
		String fileName = null;
		int p = url.lastIndexOf("/");
		fileName = url.substring(p + 1, url.length());
		return fileName;
	}
	
	public static String getUA(Activity activity) {
		String ua = null;
		WebView webview;
		webview = new WebView(activity);
		webview.layout(0, 0, 0, 0);
		WebSettings settings = webview.getSettings();
		ua = settings.getUserAgentString();
		AspLog.d("ua", ua);
		return ua;
	}
}
